import 'dart:ui';

import 'package:flutter/widgets.dart';
import 'cut_canvas_controller.dart';
import '../core/cut_canvas_internal.dart';
import '../utils/common_utils.dart';

import '../cut_canvas.dart';
import 'cut_canvas_scalestate_controller.dart';

/// [CutCanvasInternalWidget]的代理类
///
/// 只处理布局相关的逻辑和所有controller状态管理
///
mixin CutCanvasControllerDelegate on State<CutCanvasInternalWidget> {
  CutCanvasControllerBase get controller => widget.controller;

  CutCanvasScaleStateController get scaleStateController =>
      widget.scaleStateController;

  ScaleBoundaries scaleBoundaries;

  ScaleStateCycle get scaleStateCycle => widget.scaleStateCycle;

  Alignment get basePosition => widget.basePosition;
  Function(double nextScale) _animateScale;

//  bool markScaleRecalculate = true;

  void addControllerListeners() {
    controller.addIgnorableListener(_blindScaleListener);
    scaleStateController.addIgnorableListener(_blindScaleStateListener);
  }

  void _blindScaleStateListener() {
    if (!scaleStateController.hasChanged) {
      return;
    }
    if (_animateScale == null || scaleStateController.isZooming) {
      controller.setScaleInvisibly(scale);
      return;
    }
//    final double prevScale = controller.scale ??
//        getScaleForScaleState(
//          scaleStateController.prevScaleState,
//          scaleBoundaries,
//        );

    final double nextScale = getScaleForScaleState(
      scaleStateController.scaleState,
      scaleBoundaries,
    );

    _animateScale(nextScale);
  }

  void addAnimateOnScaleStateUpdate(void animateScale(double nextScale)) {
    _animateScale = animateScale;
  }

  void _blindScaleListener() {
//    if (!widget.bouncingScroll) {
//      controller.position = clampPosition();
//    }
//    if (controller.scale == controller.prevValue.scale) {
//      return;
//    }
//    final CanvasScaleState newScaleState =
//        (scale > scaleBoundaries.initialScale)
//            ? CanvasScaleState.zoomedIn
//            : CanvasScaleState.zoomedOut;
//
//    scaleStateController.setInvisibly(newScaleState);
  }

  Offset get position => controller.position;

  double get scale {
    final scaleEmpty = controller.scale == null;
    if (scaleEmpty) {
      final newScale = getScaleForScaleState(
        scaleStateController.scaleState,
        scaleBoundaries,
      );
      scale = newScale;
      return scale;
    }
    return controller.scale;
  }

  bool _isSimilarValue(double a, double b) {
    return (a - b).abs() < 0.002;
  }

  set scale(double scale) => controller.setScaleInvisibly(scale);

  void updateMultiple({
    Offset position,
    double scale,
    double rotation,
    Offset rotationFocusPoint,
  }) {
    controller.updateMultiple(
        position: position,
        scale: scale,
        rotation: rotation,
        rotationFocusPoint: rotationFocusPoint);
  }

  void updateScaleStateFromNewScale(double newScale) {
    CanvasScaleState newScaleState = CanvasScaleState.initial;
    if (scale != scaleBoundaries.initialScale) {
      newScaleState = (newScale > scaleBoundaries.initialScale)
          ? CanvasScaleState.zoomedIn
          : CanvasScaleState.zoomedOut;
    }
    scaleStateController.setInvisibly(newScaleState);
  }

  void nextScaleState() {
    final CanvasScaleState scaleState = scaleStateController.scaleState;
    if (scaleState == CanvasScaleState.zoomedIn ||
        scaleState == CanvasScaleState.zoomedOut) {
      scaleStateController.scaleState = scaleStateCycle(scaleState);
      return;
    }
    final double originalScale = getScaleForScaleState(
      scaleState,
      scaleBoundaries,
    );

    double prevScale = originalScale;
    CanvasScaleState prevScaleState = scaleState;
    double nextScale = originalScale;
    CanvasScaleState nextScaleState = scaleState;
    do {
      prevScale = nextScale;
      prevScaleState = nextScaleState;
      nextScaleState = scaleStateCycle(prevScaleState);
      nextScale = getScaleForScaleState(nextScaleState, scaleBoundaries);
    } while (prevScale == nextScale && scaleState != nextScaleState);

    if (originalScale == nextScale) {
      return;
    }
    scaleStateController.scaleState = nextScaleState;
  }

  CornersRange cornersX({double scale}) {
    final double _scale = scale ?? this.scale;

    final double computedWidth = scaleBoundaries.childSize.width * _scale;
    final double screenWidth = scaleBoundaries.outerSize.width;

    final double positionX = basePosition.x;
    final double widthDiff = computedWidth - screenWidth;

    final double minX = ((positionX - 1).abs() / 2) * widthDiff * -1;
    final double maxX = ((positionX + 1).abs() / 2) * widthDiff;

    return CornersRange(minX, maxX);
  }

  CornersRange cornersY({double scale}) {
    final double _scale = scale ?? this.scale;

    final double computedHeight = scaleBoundaries.childSize.height * _scale;
    final double screenHeight = scaleBoundaries.outerSize.height;

    final double positionY = basePosition.y;
    final double heightDiff = computedHeight - screenHeight;

    final double minY = ((positionY - 1).abs() / 2) * heightDiff * -1;
    final double maxY = ((positionY + 1).abs() / 2) * heightDiff;
    return CornersRange(minY, maxY);
  }

  Offset clampPosition({Offset position, double scale}) {
    final double _scale = scale ?? this.scale;
    final Offset _position = position ?? this.position;
    final double computedWidth = scaleBoundaries.childSize.width * _scale;
    final double computedHeight = scaleBoundaries.childSize.height * _scale;

    final double screenWidth = scaleBoundaries.outerSize.width;
    final double screenHeight = scaleBoundaries.outerSize.height;

    //print('position: $_position, size: ($computedWidth, $computedHeight)');
    double _dx = _position.dx, _dy = _position.dy;
    if (computedWidth < screenWidth) {
      _dx = 0;
    } else {
      double deltaX = (screenWidth - computedWidth) / 2;
      deltaX = deltaX.abs();
      _dx = _dx.clamp(-deltaX, deltaX);
    }

    if (computedHeight < screenHeight) {
      _dy = 0;
    } else {
      double deltaY = (screenHeight - computedHeight) / 2;
      deltaY = deltaY.abs();
      _dy = _dy.clamp(-deltaY, deltaY);
    }

    return Offset(_dx, _dy);
  }

  Offset clampPositionOutSize({Offset position, double scale}) {
    final double _scale = scale ?? this.scale;
    final Offset _position = position ?? this.position;
    final double computedWidth = scaleBoundaries.childSize.width * _scale;
    final double computedHeight = scaleBoundaries.childSize.height * _scale;

    final double screenWidth = scaleBoundaries.outerSize.width;
    final double screenHeight = scaleBoundaries.outerSize.height;
    double finalX = _position.dx.clamp(
      -screenWidth / 2 -
          computedWidth * 0.5 +
          scaleBoundaries.childSize.width * 0.05,
      screenWidth / 2 +
          computedWidth * 0.5 -
          scaleBoundaries.childSize.width * 0.05,
    );
    double finalY = _position.dy.clamp(
      -screenHeight / 2 -
          computedHeight * 0.5 +
          scaleBoundaries.childSize.height * 0.05,
      screenHeight / 2 +
          computedHeight * 0.5 -
          scaleBoundaries.childSize.height * 0.05,
    );
    return Offset(finalX, finalY);
  }

  @override
  void dispose() {
    _animateScale = null;
    controller.removeIgnorableListener(_blindScaleListener);
    scaleStateController.removeIgnorableListener(_blindScaleStateListener);
    super.dispose();
  }
}
